Day15 TesorFlow.js MobileNet+KNN
昨天已經引入MobileNet能對影像是什麼進行簡單的辨識
不過這個模型是由ImageNet上面的資料訓練出來的,所以他能識別的類別,僅限於他所標籤過的類別,所以必須在這個模型中額外增加相對應的照片與標籤,來分辨提供我們想分辨的照片,並根據提供的標籤進行分類。
為了要提供我們想提供的分類器,首先要做一個分類器,這裡用的是最簡單的分類方法KNN(k-nearest neighbors)
k-nearest neighbors顧名思義就是找最近的k個鄰近點的分類,來判斷這個點的分類是什麼
以下圖為例
圖片取自KNN Classification using Scikit-learn
若設定K=3的時候,?最近的三個分類分別是2個B、1個A,所以?的分類是Class B
若設定K=7的時候,?最近的三個分類分別是3個B、4個A,所以?的分類是Class A
KNN的想法其實還蠻直覺的,就是單純的物以類聚、要去吃飯就去最多人排隊的那家,先猜多的那邊的道理
不過KNN還是有以下問題要處理
1.K值多少
看上面的範例就知道K的選擇會影響最終這個分類的結果
大致上來講可以說K越小,越擬合現有的分類與標籤集合
越大就越模糊
想想K=1的情況,就會變成根據現有標籤分類直接劃分,可能會過擬和
想想K=N(所有資料標籤數)-1,就會變成哪個分類標籤比較多,所有預測的值都是這個分類
2.距離怎麼取
剛剛上面的圖,單純就是取歐基里德距離去計算,但要如何計算距離,其實跟分類方式息息相關,也就是特徵選擇有關,並且這些距離有可能因為多維度的關係而讓歐基里德距離並不是代表分類的關聯
比如說下圖
圖片取自Manifold-based tools: ISOMAP algorithm
藍色表示x1與x2的歐基里德距離,但實際他們在資料中的分布情況,也就是兩者之間的關聯性,並無法靠歐基里德距離來評估。
簡單來說,在這張圖的左上角取個x3,歐基里德距離來看可能比較靠近x2,但以資料分布來看,這樣做分類肯定是有問題的
也就是從不同維度來看問題,會看到不同的答案
3.計算
因為要在全部樣本中找到最近的點,不考慮空間複雜度,至少要走一遍計算距離存起來O(N),O(N)看起來還好,但別忘了隨著維度的增加,計算距離的計算量也會隨之上升,走過存起來的矩陣找出前k個解最壞情況O(kN),其實是個蠻耗時的工作,雖然實務上,會用資料結構簡化這個部分,像是KD樹
基本上今天竟是把KNN加進昨天的程式裡
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>mobilenet</title>
<!-- TensorFlow.js Core -->
<script
src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js">
</script>
<!-- TensorFlow.js mobilenet model -->
<script
src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet">
</script>
<!-- Load KNN Classifier -->
<script
src="https://cdn.jsdelivr.net/npm/@tensorflow-models/knn-classifier">
</script>
<style>
article,
article p {
display: inline;
margin-right: 50px
}
article img {
width: 200px;
height: 200px;
}
div img {
width: 100px;
height: 100px;
}
</style>
</head>
<body>
<h3>dog img</h3>
<div id="img_dog">
<img crossOrigin src="https://i.imgur.com/WRitB1p.jpg" />
<img crossOrigin src="https://i.imgur.com/Uw6XM2g.jpg" />
<img crossOrigin src="https://i.imgur.com/ndjoDO3.jpg" />
</div>
<h3>cat img</h3>
<div id="img_cat">
<img crossOrigin src="https://i.imgur.com/CzXTtJV.jpg" />
<img crossOrigin src="https://i.imgur.com/oDVOgO2.jpg" />
<img crossOrigin src="https://i.imgur.com/XQsRZa2.jpg" />
</div>
<h3>test case</h3>
<article>
<img id="img_test1"
crossOrigin src="https://i.imgur.com/aKndIcZ.jpg"/>
<p id="result1"></p>
</article>
<article>
<img id="img_test2"
crossOrigin src="https://i.imgur.com/uSqV5vg.jpg"/>
<p id="result2"></p>
</article>
<script>
async function app() {
//load knn & mobilenet
const knn = knnClassifier.create();
const mobilenet_model = await mobilenet.load();
//compute dog class embedding then add to knn
const img_dog = document.getElementById('img_dog').children;
for (const img of img_dog) {
let img_dog_tensor = tf.browser.fromPixels(img);
let dog_embedding =
mobilenet_model.infer(img_dog_tensor, true);
knn.addExample(dog_embedding, "dog");
}
//compute cat class embedding then add to knn
const img_cat = document.getElementById('img_cat').children;
for (const img of img_cat) {
const img_cat_tensor = tf.browser.fromPixels(img);
const cat_embedding =
mobilenet_model.infer(img_cat_tensor, true);
knn.addExample(cat_embedding, "cat");
}
//compute test1 embedding
const img_test1 = document.getElementById('img_test1');
const img_test_tensor1 = tf.browser.fromPixels(img_test1);
const test_embedding1 =
mobilenet_model.infer(img_test_tensor1, true);
//compute test2 embedding
const img_test2 = document.getElementById('img_test2');
const img_test_tensor2 = tf.browser.fromPixels(img_test2);
const test_embedding2 =
mobilenet_model.infer(img_test_tensor2, true);
//classify two test img
const result1 = await knn.predictClass(test_embedding1);
const result2 = await knn.predictClass(test_embedding2);
document.getElementById('result1').innerHTML = result1.label;
document.getElementById('result2').innerHTML = result2.label;
console.log(result1);
console.log(result2);
}
app();
</script>
</body>
</html>
有了自己的貓狗辨識器了